// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "VmiTcpServerSocket.h"
#include <arpa/inet.h>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <thread>
#include <unistd.h>
#include "HWSecure/include/securec.h"
#include "Log/logging.h"

using namespace std;

/*
 * @功能描述：设置线程名字，由socket句柄构造
 * @参数 [in] fd：socket句柄
 */
static void SetThreadName(int fd)
{
    std::string threadName = "TA" + std::to_string(fd);
    if (prctl(PR_SET_NAME, threadName.c_str()) < 0) {
        ERR("prctl error, errno: (%d): %s", errno, strerror(errno));
    }
}

/*
 * @功能描述：新连接接收线程，将新连接的socket封装成VmiTcpSocket对象
 * @参数 [in] tcpServerSocket：服务端VmiTcpServerSocket指针
 */
void AcceptTaskEntry(VmiTcpServerSocket *tcpServerSocket)
{
    int listenFd = tcpServerSocket->GetFd();
    INFO("AcceptTaskEntry: listenFd(%d)", listenFd);
    SetThreadName(listenFd);

    struct sockaddr_in clientAddr = {};
    socklen_t len = sizeof(clientAddr);

    do {
        INFO("waiting client....");
        int clientSockFd = accept(listenFd, reinterpret_cast<struct sockaddr *>(&clientAddr), &len);
        if (clientSockFd == -1) {
            ERR("TCP server(%d) accept failed, try again... errno(%d): %s", listenFd, errno, strerror(errno));
            continue;
        }

        INFO("Security Audit: TCP server(%d) accept new client(%d), remote ip is %s", listenFd, clientSockFd, inet_ntoa(clientAddr.sin_addr));
        unsigned int remoteIp = htonl(clientAddr.sin_addr.s_addr);
        unsigned short int remotePort = ntohs(clientAddr.sin_port);
        VmiSocketAddressGroup address(remoteIp, remotePort, 0, 0);

        // 封装成VmiTcpSocket对象
        std::shared_ptr<VmiTcpSocket> newSocket(new (std::nothrow) VmiTcpSocket(clientSockFd, address, 0));
        if (newSocket == nullptr) {
            ERR("Fatal error: create vmiTcpSocket failed!");
            (void)close(clientSockFd);
            continue;
        }

        newSocket->SetStatus(SOCKET_STATUS_RUNNING);
        tcpServerSocket->NotifyServerService(newSocket);
    } while (tcpServerSocket->GetListenStatus() == SOCKET_STATUS_RUNNING);

    INFO("Tcp server(%d) accept thread exit!", listenFd);
    tcpServerSocket->SetStatus(SOCKET_STATUS_EXIT);
    tcpServerSocket->EventNotice();
}

/*
 * @功能描述：启动监听线程
 */
int VmiTcpServerSocket::StartAcceptThread()
{
    INFO("starting accept thread...");
    // 先设置成运行态
    m_listenStatus = SOCKET_STATUS_RUNNING;
    m_acceptTask = new (std::nothrow) thread(AcceptTaskEntry, this);
    if (m_acceptTask == nullptr) {
        ERR("error: create accept thread failed!");
        CloseSocket();
        m_listenStatus = SOCKET_STATUS_INIT;
        return SOCK_RET_FAILED;
    }
    return SOCK_RET_SUCCESS;
}

/*
 * @功能描述：启动TCP服务端
 * @返回值：成功返回SOCK_RET_SUCCESS，失败返回SOCK_RET_FAILED
 */
int VmiTcpServerSocket::Start()
{
    if (m_status == SOCKET_STATUS_RUNNING) {
        INFO("already started");
        return SOCK_RET_SUCCESS;
    }

    // 创建socket
    INFO("Security Audit: create server socket...");
    m_fd = socket(PF_INET, SOCK_STREAM, 0);
    if (m_fd < 0) {
        ERR("create socket failed errno: (%d): %s", errno, strerror(errno));
        return SOCK_RET_FAILED;
    }

    int reuse = 1;
    if (setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
        ERR("set option REUSER failed, errno: (%d): %s", errno, strerror(errno));
        CloseSocket();
        return SOCK_RET_FAILED;
    }

    if ((m_socketOption & SOCKET_OPTION_BITSET_NODELAY) != 0) {
        int enable = 1;
        if (setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)) < 0) {
            ERR("set option TCP_NODELAY failed, errno: (%d): %s", errno, strerror(errno));
            CloseSocket();
            return SOCK_RET_FAILED;
        }
    }

    struct sockaddr_in localAddr = {};
    if (memset_s(&localAddr, sizeof(localAddr), 0, sizeof(localAddr)) != EOK) {
        ERR("error: memset localAddr failed");
        CloseSocket();
        return SOCK_RET_FAILED;
    }

    localAddr.sin_family = AF_INET;
    localAddr.sin_addr.s_addr = htonl(m_localIp);
    localAddr.sin_port = htons(m_localPort);

    // 绑定socket到指定的地址及端口
    INFO("Security Audit: bind...");
    if (::bind(m_fd, reinterpret_cast<struct sockaddr *>(&localAddr), sizeof(localAddr)) != 0) {
        ERR("error: bind socket failed errno: (%d) %s", errno, strerror(errno));
        CloseSocket();
        return SOCK_RET_FAILED;
    }

    // 监听TCP端口
    INFO("Security Audit: listen...");
    if (listen(m_fd, MAX_LISTEN_NUM) != 0) {
        ERR("error: listen socket failed errno: (%d) %s", errno, strerror(errno));
        CloseSocket();
        return SOCK_RET_FAILED;
    }

    m_status = SOCKET_STATUS_RUNNING;
    return StartAcceptThread();
}

/*
 * @功能描述：TCP服务端套接字构造函数
 * @参数 [in] localIp：本地IP
 * @参数 [in] localPort：本地端口
 * @参数 [in] service：需要注册的新连接处理对象
 */
VmiTcpServerSocket::VmiTcpServerSocket(unsigned int localIp, unsigned short int localPort,
    std::shared_ptr<VmiServerService> service)
    : VmiTcpSocket(INVALID_FD, VmiSocketAddressGroup(0, 0, localIp, localPort), 0), m_listenStatus(SOCKET_STATUS_INIT),
      m_acceptTask(nullptr), m_service(service)
{
}//lint !e1401

/*
 * @功能描述：TCP服务端套接字构造函数
 * @参数 [in] socketOption：需设置的socket选项
 * @参数 [in] localIp：本地IP
 * @参数 [in] localPort：本地端口
 * @参数 [in] service：需要注册的新连接处理对象
 */
VmiTcpServerSocket::VmiTcpServerSocket(unsigned int socketOption, unsigned int localIp,
    unsigned short int localPort, std::shared_ptr<VmiServerService> service)
    : VmiTcpSocket(INVALID_FD, VmiSocketAddressGroup(0, 0, localIp, localPort), socketOption),
      m_listenStatus(SOCKET_STATUS_INIT), m_acceptTask(nullptr), m_service(service)
{
}//lint !e1401

/*
 * @功能描述：析构，关闭套接字，停止连接监听线程
 */
VmiTcpServerSocket::~VmiTcpServerSocket()
{
    // 关闭listen socket
    CloseSocket();
    // 设置线程退出标志
    m_listenStatus = SOCKET_STATUS_EXIT;
    // 等待线程退出
    INFO("wait accept thread to exit...");
    if (m_acceptTask != nullptr) {
        m_acceptTask->join();
        delete m_acceptTask;
        m_acceptTask = nullptr;
    }
    INFO("accept thread exited");
}

/*
 * @功能描述：通知VmiServerService对象有新连接接入
 * @参数 [in] socket：新连接的VmiTcpSocket智能指针
 */
void VmiTcpServerSocket::NotifyServerService(std::shared_ptr<VmiTcpSocket> socket)
{
    m_service->Callback(std::dynamic_pointer_cast<VmiSocket>(socket));
}

/*
 * @功能描述：获取监听状态
 * @返回值：监听状态
 */
int VmiTcpServerSocket::GetListenStatus()
{
    return m_listenStatus;
}
